/*
 *      keybindingsdialog.cpp
 *
 *      Copyright 2011 David Vachulka <david@konstrukce-cad.com>
 *      Copyright Roland Baudin <roland65@free.fr>
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 3 of the License, or
 *      (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */

#include "keybindingsdialog.h"
#include "dxutils.h"
#include "config.h"
#include "icons.h"
#include "i18n.h"
#include "dxirc.h"

#define preferences dxirc::instance()->getPreferences()

FXDEFMAP(BindingsDialog) BindingsDialogMap[]=
{
    FXMAPFUNC(SEL_KEYPRESS,0,BindingsDialog::onCmdKeyPress),
};

FXIMPLEMENT(BindingsDialog, FXDialogBox, BindingsDialogMap, ARRAYNUMBER(BindingsDialogMap))

BindingsDialog::BindingsDialog(FXWindow *win, FXString input, FXString message, FXString title, FXIcon *icon):
        FXDialogBox(win, title, DECOR_TITLE|DECOR_BORDER|DECOR_STRETCHABLE)
{
    FXHorizontalFrame *buttons=new FXHorizontalFrame(this, PACK_UNIFORM_WIDTH|LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X, 0,0,0,0,10,10,5,5);
    new dxEXButton(buttons,_("&Accept"), NULL, this, ID_ACCEPT, FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT, 0,0,0,0,10,10,2,2);
    new dxEXButton(buttons,_("&Cancel"), NULL, this, ID_CANCEL, FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT, 0,0,0,0,10,10,2,2);
    new FXHorizontalSeparator(this, LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|SEPARATOR_GROOVE);
    FXVerticalFrame *contents=new FXVerticalFrame(this, LAYOUT_SIDE_TOP|FRAME_NONE|LAYOUT_FILL_X|LAYOUT_FILL_Y);
    FXMatrix *matrix = new FXMatrix(contents,2,MATRIX_BY_COLUMNS|LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y);
    new FXLabel(matrix, "", icon, LAYOUT_LEFT);
    new FXLabel(matrix, message.text(), NULL, JUSTIFY_LEFT|LAYOUT_CENTER_Y|LAYOUT_FILL_COLUMN|LAYOUT_FILL_ROW);
    keylabel=new FXLabel(contents, input.text(), NULL, LAYOUT_CENTER_X);
}

void BindingsDialog::create()
{
    FXDialogBox::create();
}

//A key was pressed
long BindingsDialog::onCmdKeyPress(FXObject* sender,FXSelector sel,void* ptr)
{
    FXEvent* event=(FXEvent*)ptr;
    switch(event->code)
    {
    case KEY_Escape:
        handle(this,FXSEL(SEL_COMMAND,ID_CANCEL),NULL);
        return 1;
    case KEY_KP_Enter:
    case KEY_Return:
        handle(this,FXSEL(SEL_COMMAND,ID_ACCEPT),NULL);
        return 1;
    default:
        FXTopWindow::onKeyPress(sender,sel,ptr);
        // Get and display key binding string from user input
        FXString key = dxutils::getKeybinding(event);
        keylabel->setText(key);
        return 1;
    }
    return 0;
}

FXDEFMAP(KeybindingsDialog) KeybindingsDialogMap[] = {
    FXMAPFUNC(SEL_COMMAND,          KeybindingsDialog_CANCEL,       KeybindingsDialog::onCancel),
    FXMAPFUNC(SEL_CLOSE,            0,                              KeybindingsDialog::onCancel),
    FXMAPFUNC(SEL_COMMAND,          KeybindingsDialog_SAVECLOSE,    KeybindingsDialog::onSaveClose),
    FXMAPFUNC(SEL_KEYPRESS,         0,                              KeybindingsDialog::onKeyPress),
    FXMAPFUNC(SEL_DOUBLECLICKED,    KeybindingsDialog_LIST,         KeybindingsDialog::onKBList)
};

FXIMPLEMENT(KeybindingsDialog, FXDialogBox, KeybindingsDialogMap, ARRAYNUMBER(KeybindingsDialogMap))

KeybindingsDialog::KeybindingsDialog(FXWindow *owner)
        : FXDialogBox(owner, _("Key bindings list"), DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE, 0,0,0,0, 0,0,0,0, 0,0)
{
    m_keybindings = dxirc::instance()->getTempKeybindings();
    m_changed = FALSE;
    m_nonchangeableBindings.append("Tab");
    m_nonchangeableBindings.append("Up");
    m_nonchangeableBindings.append("Down");
    m_nonchangeableBindings.append("F3");
    m_nonchangeableBindings.append("N");
    m_nonchangeableBindings.append("P");
    m_nonchangeableBindings.append("I");
    m_nonchangeableBindings.append("J");
    m_nonchangeableBindings.append("K");
    m_nonchangeableBindings.append("L");

    m_contents = new FXVerticalFrame(this, LAYOUT_FILL_X|LAYOUT_FILL_Y);

    m_tableframe = new FXVerticalFrame(m_contents, LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_THICK);
    m_list = new FXIconList(m_tableframe, this, KeybindingsDialog_LIST, HSCROLLER_NEVER|ICONLIST_BROWSESELECT|ICONLIST_DETAILED|LAYOUT_FILL_X|LAYOUT_FILL_Y);
    m_list->appendHeader(_("Command"), NULL, 430);
    m_list->appendHeader(_("Key binding"), NULL, 150);
    m_list->setSortFunc(ascendingActionName);

    m_buttonframe = new FXHorizontalFrame(m_contents, LAYOUT_FILL_X);
    m_buttonCancel = new dxEXButton(m_buttonframe, _("&Cancel"), NULL, this, KeybindingsDialog_CANCEL, FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT, 0,0,0,0, 10,10,2,2);
    m_buttonSaveClose = new dxEXButton(m_buttonframe, _("&Save&&Close"), NULL, this, KeybindingsDialog_SAVECLOSE, BUTTON_INITIAL|BUTTON_DEFAULT|FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT, 0,0,0,0, 10,10,2,2);
}

KeybindingsDialog::~KeybindingsDialog()
{
}

void KeybindingsDialog::create()
{
    FXDialogBox::create();
    setWidth(600);
    setHeight(400);
    updateList();
}

long KeybindingsDialog::onCancel(FXObject*, FXSelector, void*)
{
    getApp()->stopModal(this,FALSE);
    hide();
    return 1;
}

long KeybindingsDialog::onSaveClose(FXObject*, FXSelector, void*)
{
    if(m_changed)
    {
        dxirc::instance()->setTempKeybindings(m_keybindings);
        if(dxEXMessageBox::question(this, MBOX_YES_NO, _("Restart"), _("Key bindings will be changed after restart.\nRestart dxirc now?"))==MBOX_CLICKED_YES)
        {
            dxirc::instance()->handle(this, FXSEL(SEL_COMMAND, dxirc_RESTART), NULL);
        }
        m_changed = FALSE;
    }
    getApp()->stopModal(this,TRUE);
    hide();
    return 1;
}

long KeybindingsDialog::onKeyPress(FXObject *sender, FXSelector sel, void *ptr)
{
    if(FXTopWindow::onKeyPress(sender,sel,ptr)) return 1;
    if(((FXEvent*)ptr)->code == KEY_Escape)
    {
        handle(this,FXSEL(SEL_COMMAND,ID_CANCEL),NULL);
        return 1;
    }
    return 0;
}

long KeybindingsDialog::onKBList(FXObject*, FXSelector, void *ptr)
{
    FXint index = (FXint)(FXival)ptr;
    if(index<0) return 0;
    FXString action, kb, key;
    action = m_list->getItemText(index).before('\t');
    kb = m_list->getItemText(index).rafter('\t');
    for(StringIt it=m_keybindings.begin(); it!=m_keybindings.end(); it++)
    {
        if(action == preferences.getBindingName((*it).first))
        {
            key = (*it).first;
            break;
        }
    }
    FXString message;
    message.format(_("Press the combination of keys you want to use for the action: %s"), action.text());
    message = message+ "\n" + _("[Press space to disable the key binding for this action]");
    BindingsDialog *dialog = new BindingsDialog(this, kb, message, _("Modify Key binding"), ICO_KEYBINDINGS);
    if(dialog->execute())
    {
        kb = dialog->getKey();
        if(kb == "Space") kb = "";
        if(bindingExist(key, kb))
        {
            dxEXMessageBox::error(this, MBOX_OK, _("Error"), _("The key binding %s is already used.\nYou should erase the existing key binding before assigning it again."), kb.text());
        }
        else
        {
            m_changed = TRUE;
            m_keybindings.erase(key);
            m_keybindings.insert(StringPair(key, kb));
            updateList();
        }
    }
    delete dialog;
    return 1;
}

FXbool KeybindingsDialog::bindingExist(const FXString &key, const FXString &kb)
{
    if(kb.empty()) return FALSE;
    for(FXint i=0; i<m_nonchangeableBindings.no(); i++)
    {
        if(comparecase(kb, m_nonchangeableBindings[i])==0)
            return TRUE;
    }
    StringIt it;
    for(it=m_keybindings.begin(); it!=m_keybindings.end(); it++)
    {
        if(kb==(*it).second)
        {
            if(key==(*it).first) return FALSE; //nothing changed thefore FALSE
            else return TRUE;
        }
    }
    return FALSE;
}

void KeybindingsDialog::updateList()
{
    m_list->clearItems();
    StringIt it;
    for(it=m_keybindings.begin(); it!=m_keybindings.end(); it++)
        m_list->appendItem(preferences.getBindingName((*it).first)+TAB+(*it).second);
    m_list->sortItems();
}

// Compare sectioned strings
FXint KeybindingsDialog::compareSection(const FXchar *p, const FXchar* q, FXint s)
{
    register FXint c1,c2,x;
    for (x=s; x && *p; x-=(*p++=='\t'));
    for (x=s; x && *q; x-=(*q++=='\t'));
    do
    {
        c1=FXuchar(*p++);
        c2=FXuchar(*q++);
    }
    while ('\t'<c1 && (c1==c2));
    return c1-c2;
}

// Sort functions
FXint KeybindingsDialog::ascendingActionName(const FXIconItem *a, const FXIconItem *b)
{
    return compareSection(a->getText().text(), b->getText().text(), 0);
}

FXint KeybindingsDialog::descendingActionName(const FXIconItem *a, const FXIconItem *b)
{
    return compareSection(b->getText().text(), a->getText().text(), 0);
}

FXint KeybindingsDialog::ascendingKeybinding(const FXIconItem *a, const FXIconItem *b)
{
    return compareSection(a->getText().text(), b->getText().text(), 1);
}

FXint KeybindingsDialog::descendingKeybinding(const FXIconItem *a, const FXIconItem *b)
{
    return compareSection(b->getText().text(), a->getText().text(), 1);
}
